using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;

namespace Tsanie.ShellExtension {

    /// <summary>
    /// Base class for ShellExecute hook handlers
    /// </summary>
    public abstract class ShellExecuteHookBase : IShellExecuteHookA, IShellExecuteHookW {

        protected ShellExecuteHookBase() { }

        #region - Public Methods -

        /// <summary>
        /// Notifies that an application is about to be executed.
        /// </summary>
        /// <param name="ExecInfo">Information about the process that will be execute.</param>
        /// <returns></returns>
        public abstract bool OnExecute(ShellExecuteInfo execInfo);

        #endregion

        #region - IShellExecuteHook Methods -

        int IShellExecuteHookA.Execute(ref SHELLEXECUTEINFO_ pei) {
            ShellExecuteInfo ExecInfo = default(ShellExecuteInfo);

            ExecInfo.hIcon = pei.hIcon_hMonitor;
            ExecInfo.hInstApp = pei.hInstApp;
            ExecInfo.hKeyClass = pei.hKeyClass;
            ExecInfo.HotKey = pei.dwHotKey;
            ExecInfo.hWnd = pei.hWnd;
            ExecInfo.Mask = pei.fMask;
            ExecInfo.ShowMode = pei.nShow;
            ExecInfo.IDList = pei.lpIDList;
            ExecInfo.Class = Marshal.PtrToStringAnsi(pei.lpClass);
            ExecInfo.File = Marshal.PtrToStringAnsi(pei.lpFile);
            ExecInfo.Directory = Marshal.PtrToStringAnsi(pei.lpDirectory);
            ExecInfo.Parameters = Marshal.PtrToStringAnsi(pei.lpParameters);
            ExecInfo.Verb = Marshal.PtrToStringAnsi(pei.lpVerb);

            if (OnExecute(ExecInfo)) {
                // Cancel the execution
                pei.hProcess = new IntPtr(33);
                return 0;
            }
            return 1;
        }

        int IShellExecuteHookW.Execute(ref SHELLEXECUTEINFO_ pei) {
            ShellExecuteInfo ExecInfo = default(ShellExecuteInfo);

            ExecInfo.hIcon = pei.hIcon_hMonitor;
            ExecInfo.hInstApp = pei.hInstApp;
            ExecInfo.hKeyClass = pei.hKeyClass;
            ExecInfo.HotKey = pei.dwHotKey;
            ExecInfo.hWnd = pei.hWnd;
            ExecInfo.Mask = pei.fMask;
            ExecInfo.ShowMode = pei.nShow;
            ExecInfo.IDList = pei.lpIDList;
            ExecInfo.Class = Marshal.PtrToStringUni(pei.lpClass);
            ExecInfo.File = Marshal.PtrToStringUni(pei.lpFile);
            ExecInfo.Directory = Marshal.PtrToStringUni(pei.lpDirectory);
            ExecInfo.Parameters = Marshal.PtrToStringUni(pei.lpParameters);
            ExecInfo.Verb = Marshal.PtrToStringUni(pei.lpVerb);

            if (OnExecute(ExecInfo)) {
                // Cancel the execution
                pei.hProcess = new IntPtr(33);
                return 0;
            }
            return 1;
        }

        #endregion

        #region - COM Registration -

        [ComRegisterFunction()]
        private static void ComReg(Type t) {
            // Get the class ClassId 
            string ClassId = Common.GetClassGuid(t);
            // Register the class as an approved extension
            using (RegistryKey key = Registry.LocalMachine.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved")) {
                key.SetValue(ClassId, t.Name);
                key.Close();
            }
            // Register the hook
            using (RegistryKey key = Registry.LocalMachine.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks")) {
                key.SetValue(ClassId, t.Name);
                key.Close();
            }
        }

        [ComUnregisterFunction()]
        private static void ComUnreg(Type t) {
            // Get the class ClassId 
            string ClassId = Common.GetClassGuid(t);
            // Unregister the class as an approved extension
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved")) {
                key.DeleteValue(ClassId);
                key.Close();
            }
            // Unregister the class as an ShellExecuteHook
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks")) {
                key.DeleteValue(ClassId);
                key.Close();
            }
        }

        #endregion

    }

}
